﻿using LeRenard.ScriptWars.Model.Scripting.Definitions;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;

namespace LeRenard.ScriptWars.Model.Scripting
{
    /// <summary>
    /// Implementation of <see cref="IScript"/>.
    /// </summary>
    public sealed class Script : IScript
    {
        #region Static
        /// <summary>
        /// Loads a <see cref="IScript"/> from file.
        /// </summary>
        /// <param name="file">The file that contains the script.</param>        
        public static IScript FromFile(FileInfo file)
        {
            ThrowIf.Null(() => file);
            ThrowIf.FileNotFound(() => file);

            var isSectionHeader = false;
            var currentSection = "";
            var scriptName = "";
            var scriptColor = "";
            var lineNumber = 1;
            var operations = new List<IOperation>();

            using (var sr = file.OpenText()) {
                while (!sr.EndOfStream) {
                    var currentLine = sr.ReadLine();

                    if (currentLine.StartsWith("#")) {
                        isSectionHeader = true;
                        currentSection = currentLine.Substring(1, currentLine.Length - 1).ToUpper();
                    } else {
                        isSectionHeader = false;
                    }

                    if (!isSectionHeader) {
                        if (currentSection.Equals("GLOBAL")) {
                            if (currentLine.StartsWith("Name:")) {
                                scriptName = currentLine.Replace("Name: ", "").RemoveMultipleWhitespaces();
                            }
                            if (currentLine.StartsWith("Color:")) {
                                scriptColor = currentLine.Replace("Color: ", "").RemoveMultipleWhitespaces();
                            }
                        }

                        if (currentSection.Equals("ALGORITHM")) {
                            var operationParts = currentLine.Split(' ');
                            
                            var operationDefinition = OperationDefinitionRepository
                                .OperationDefinitions
                                .Where(od => od.Name.Equals(operationParts[0]))
                                .Single();

                            var operationParameters = new List<IOperationParameter>();
                            var parameterNumber = 1;
                            if (operationDefinition.HasParameters) {
                                foreach (var operationParameterDefinition in operationDefinition.Parameters.Values) {
                                    var operationParameter = new OperationParameter(
                                            operationParts[parameterNumber],
                                            operationParameterDefinition 
                                        );

                                    operationParameters.Add(operationParameter);

                                    parameterNumber++;
                                }
                            }

                            var operation = new Operation(lineNumber, operationDefinition, operationParameters);
                            operations.Add(operation);

                            lineNumber++;
                        }
                    }
                }
            }

            var algorithm = new Algorithm(operations);
            var color = ColorTranslator.FromHtml(scriptColor);
            var script = new Script(scriptName, color, algorithm);

            return script;
        }
        #endregion

        #region Properties
        /// <summary>
        /// Gets the script's name.
        /// </summary>
        public string Name { get { return _name; } }
        private readonly string _name;

        /// <summary>
        /// Gets the script's desired color.
        /// </summary>
        public Color Color { get { return _color; } }
        private readonly Color _color;

        /// <summary>
        /// Gets the script's algorithm.
        /// </summary>
        public IAlgorithm Algorithm { get { return _algorithm; } }
        private readonly IAlgorithm _algorithm;
        #endregion

        #region Construction
        /// <summary>
        /// Creates a new <see cref="Script"/> object.
        /// </summary>
        /// <param name="name">The script's name.</param>
        /// <param name="algorithm">the script's algorithm.</param>
        internal Script(string name, Color color, IAlgorithm algorithm)
        {
            ThrowIf.NullOrEmpty(() => name);            
            ThrowIf.Null(() => algorithm);

            _name = name;
            _color = color;
            _algorithm = algorithm;
        }
        #endregion
    }
}
